package utils

import (
	"bytes"
	"encoding/base64"
	"math/rand"

	"github.com/fogleman/gg"
)

type Captcha struct {
	Module string
	Length int
	Width  int
	Height int
}

var (
	img   *gg.Context
	chars = "ABCDEFGHKMNPQTWYbcdefghkmnpqrtwxy23456789"
	hans  = "覆去翻来佛手柑不拔之柱逞工衒巧中宫史坐娘家水上运动蠹国耗民生产关系日削月割比例税指瑕造隙撑场面脱气聚米为谷氢氧化铝区之心长串正面人物觅帖儿大少爷相敬如宾山不厌高战争论堂哉皇哉平衡木更深人静断瓦残垣九江市行思坐想尘沙劫养汞调铅王乔履兔园册子含苞吐萼整纷剔蠹八声甘州关山飞渡麕集蜂萃灌精儿海陵仓顺嘴溜挑嫌四品松出言吐语事捷功倍无家可奔路里彩翮六翼赴死如归菊花杯除红捉緑鸡目眼郁久闾舌边音永安宫饔飧不济蜣螂转粪贞女引简笔字威望素著安济坊久梦初醒太常雅乐雪兆丰年廊庙才发号施令广种薄收深图远算性命交关谷城翁恶模样蛾扑灯蕊洞中仙画日笔品质因素额手称颂含垢藏瑕金铛大畹地拉那矫情饰貌瑞龙脑稗官说临危制变运动协会屈指延安学荒唐不经丽花长爪郎物薄情厚语言美心脏病底特律厚厚敦敦吴承恩半疯儿枕中记油果子条分缕析"
)

// 随机颜色
func RandColor(min, max int) (r, g, b int) {
	r = rand.Intn(max-min) + min
	g = rand.Intn(max-min) + min
	b = rand.Intn(max-min) + min
	return
}

// 生成图像
func (captcha *Captcha) Create() (code, base64String string, err error) {
	img = gg.NewContext(captcha.Width, captcha.Height)
	img.DrawRectangle(0, 0, float64(captcha.Width), float64(captcha.Height))
	img.SetRGB255(RandColor(180, 230))
	img.Fill()

	switch captcha.Module {
	case "string":
		code, base64String, err = captcha.RandString(captcha.Length, chars)
	case "hans":
		code, base64String, err = captcha.RandHans(captcha.Length, hans)
	default:
		code, base64String, err = captcha.RandString(captcha.Length, chars)
	}
	return
}

// 绘制干扰线
func (captcha *Captcha) DrawLine(n int) {
	for i := 0; i < n; i++ {
		x1, y1 := captcha.RandPoint()
		x2, y2 := captcha.RandPoint()
		r, g, b := RandColor(100, 180)
		w := float64(rand.Intn(3) + 1)
		img.SetRGB255(r, g, b)
		img.SetLineWidth(w)
		img.DrawLine(x1, y1, x2, y2)
		img.Stroke()
	}

}

// 绘制干扰像素
func (captcha *Captcha) DrawPix(n int) {
	for i := 0; i < n; i++ {
		x, y := captcha.RandPoint()
		r, g, b := RandColor(140, 200)
		img.SetRGB255(r, g, b)
		img.DrawPoint(x, y, float64(rand.Intn(3)))
		img.Fill()
	}
}

// 获取随机坐标
func (captcha *Captcha) RandPoint() (x, y float64) {
	x = float64(rand.Intn(captcha.Width))
	y = float64(rand.Intn(captcha.Height))
	return
}

// 绘制文字
func (captcha *Captcha) DrawText(text string) error {
	for i := 0; i < len(text); i++ {
		fontSize := rand.Float64()*5 + float64(captcha.Height/2) + 10
		err := img.LoadFontFace("config/font/alibaba.ttf", fontSize)
		if err != nil {
			return err
		}
		r, g, b := RandColor(0, 70)
		img.SetRGBA255(r, g, b, 255)
		x := float64(captcha.Width/len(text)*i) + fontSize*0.2
		y := float64(captcha.Height / 2)

		xfload := 5 - rand.Float64()*10 + x
		yfload := 5 - rand.Float64()*10 + y

		radians := 40 - rand.Float64()*80
		img.RotateAbout(gg.Radians(radians), x, y)
		img.DrawStringAnchored(text[i:i+1], xfload, yfload, 0.2, 0.5)
		img.RotateAbout(-1*gg.Radians(radians), x, y)
		img.Stroke()
	}
	return nil
}

func (captcha *Captcha) RandString(length int, source string) (string, string, error) {
	b := make([]byte, length)
	for i := range b {
		b[i] = source[rand.Intn(len(source))]
	}
	code := string(b)
	captcha.DrawLine(8)
	captcha.DrawPix(30)
	err := captcha.DrawText(code)
	if err != nil {
		return "", "", err
	}

	buf := new(bytes.Buffer)
	err = img.EncodePNG(buf)
	if err != nil {
		return "", "", err
	}

	base64String := "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())
	return code, base64String, nil
}

func (captcha *Captcha) RandHans(length int, source string) (string, string, error) {
	b := make([]byte, length)
	for i := range b {
		b[i] = source[rand.Intn(len(source))]
	}
	code := string(b)
	return code, "", nil
}
